#include "../include/web_page_query.hpp"

#include <algorithm>
#include <json/json.h>

using std::find;

namespace gcj
{

string WebPageQuery::doQuery(const string & queryStr) {
    //执行查询的入口，返回josn编码规范的字符串
    vector<string> queryWords = _splitTool->cut(queryStr);//切分传进来的查询语句

    //获取每个要查询的词语的权重
    vector<double> queryWordsWeight = getQueryWordsWeightVec(queryWords);

    //查询包含所有关键字的网页，如果关键字没有一个在索引表中，就认为没有找到
    //用一个容器保存包含所有关键字的网页的docId，以及每个关键字在该页中的权重
    vector<pair<long, vector<double>>> resultVec;   //用来存放
    string msg;
    //执行查询
    bool flag = executeQuery(queryWords, resultVec);
    if(flag) {
        //生成要发送给客户端的数据
        //根据排序算法对查询结果进行排序
        vector<long> docIdVec = webSort(queryWordsWeight, resultVec);
        msg = createJson(docIdVec, queryWords);
    }
    else {
        //匹配不到时，返回【查询不到相关网页】
        msg = returnNoAnswer();
    }
    return msg;
}

vector<double> WebPageQuery::getQueryWordsWeightVec(const vector<string> & queryWords) {
    //计算一个网页中的所有查询词的权重值，使用TF-IDF算法
    //将查询语句视为一个网页，则 TF = 1，DF就是包含该词的网页的数量
    int tf = 1;
    long cnt = _webLib->getPageLib().size();    //页面总数
    vector<double> queryWordsWeight;            //存放每一个关键词的权重
    double weightSum = 0;

    for(const string & word : queryWords) {
        //遍历关键词，结算TF-IDF值
        if(_webLib->getInvertIndexTab().find(word) == _webLib->getInvertIndexTab().cend()) {
            //如果有查询词不在索引表中，该查询词的权重设为-1
            queryWordsWeight.push_back(-1);
            return queryWordsWeight;//直接返回
        }
        //索引表中有查询词，那就计算权重
        int df = _webLib->getInvertIndexTab()[word].size();
        double tf_idf = tf * log(cnt / df) / log(2);
        queryWordsWeight.push_back(tf_idf);
        weightSum += tf_idf * tf_idf;
    }
    
    for(double & wordWeight : queryWordsWeight) {
        //归一化处理
        wordWeight = wordWeight / sqrt(weightSum);
    }
    return queryWordsWeight;
}

bool WebPageQuery::executeQuery(const vector<string> &queryWords
                        , vector<pair<long, vector<double>>> &resultVec) {
    cout << "queryWords:" << endl;
    for(auto & word : queryWords) {
        cout << word << "   ";
    }
    cout << endl;
    //执行查询
    //只要有一个关键词不在索引表，就认为没有找到相关网页
    vector<double> queryWordsWeight = getQueryWordsWeightVec(queryWords);
    if(find(queryWordsWeight.cbegin(), queryWordsWeight.cend(), -1) != queryWordsWeight.cend()) {
        cout << "not found!" << endl;
        return false;
    }

    //否则就查找所有关键词在所有页面中的权重
    //应当避免重复的统计,需要一个表记录统计过的网页
    unordered_set<long> countedId;
    //需要找到包含所有关键词的，需要直到一共有多少关键词，好进行对比
    size_t queryWordsSize = queryWords.size();

    for(auto & queryWord : queryWords) {//遍历关键词
        //一页一页的查，找到包含所有关键词的网页的id以及关键词的权重
        set<pair<long, double>> id2Weight = _webLib->getInvertIndexTab()[queryWord];
        /* cout << "id2Weight.size() = " << id2Weight.size() << endl; */

        for(auto & docid : id2Weight) {
            //遍历含有当前关键词的所有网页
            /* cout << "包含关键词的docid：" << docid << endl; */
            if(countedId.find(docid.first) != countedId.cend()) {
                //如果已经统计过，跳过该页
                continue;
            }
            //否则进行统计
            //需要统计当前id下的所有关键词的权重
            vector<double> webWordsWeight;      //用来保存当前id下的所有关键词的权重
            for(auto & otherQueryWord : queryWords) {
                set<pair<long, double>> otherId2Weight = _webLib->getInvertIndexTab()[otherQueryWord];
                for(auto & otherDocid : otherId2Weight) {
                    if(otherDocid.first == docid.first) {
                        //把同个id里的关键词的权重都找出来
                        webWordsWeight.push_back(otherDocid.second);
                    }
                }
            }
            if(webWordsWeight.size() != queryWordsSize) {
                //如果不是包含所有关键词，不记录
                continue;
            }
            //如果包含所有关键词，进行记录
            resultVec.push_back({docid.first, webWordsWeight});
            countedId.insert(docid.first);  //记录已经统计过的网页的id
        }
    }
    if(resultVec.size() > 0) {
        //有匹配的网页，返回true
        return true;
    }
    else {
        return false;
    }
}

string WebPageQuery::createJson(vector<long> & docIdVec, const vector<string> & queryWords) {
    //生成要发送给客户端的JOSN格式的字符串
    Json::Value root;  //JSON根节点，用来保存
    Json::Value webs;  //依序存放每一个网页的信息

    int idx = 0;

    for(long & docId : docIdVec) {
        //遍历排好序的查询结果docid
        shared_ptr<WebPage> webPage(_webLib->getPageLib()[docId]);
        //将网页信息存储到JSON中
        Json::Value web;
        web["docId"] = Json::Value((int)webPage->getDocID());
        web["link"] = Json::Value(webPage->getUrl());
        web["title"] = Json::Value(webPage->getTitle());

        //需要根据一定的策略生成网页的摘要
        string webContent = webPage->getContent();
        web["content"] = Json::Value(webContent);
        web["abstract"] = Json::Value(getAbstract(queryWords, webContent));
        webs.append(web);
        if(idx == 9) {
            break;
        }
        ++idx;
    }
    root["webs"] = webs;

    //把生成的JSON转换成string类型作为返回值
    string res = root.toStyledString();

    return res;
}

string WebPageQuery::getAbstract(const vector<string> &queryWords, string &webPageContent) {
    //根据关键词，提取网页的摘要，前后各取90个字节，一共60个字符
    string res;
    int pos = 0, beg = 0, end = 0;
    int sz = webPageContent.size();
    for(auto & queryWord : queryWords) {
        pos = webPageContent.find(queryWord);
        beg = (pos - 90) > 0 ? (pos - 90) : (0);
        end = (pos + 90) < sz ? (pos + 90) : (sz - 1);
        /* if(pos == -1) { */
        /*     //没有找到关键词，跳过 */
        /*     continue; */
        /* } */
        cout << "pos = " << pos << ", beg = " << beg << ", end = " << end << endl;
        string stmp = webPageContent.substr(beg, end - beg);
        /* cout << "stmp >>>> "<< stmp << endl; */
        res += stmp;
    }
    return res;
}

//用来为结果网页排序的比较器
bool com(const pair<long, double> &lhs, const pair<long, double> &rhs) {
    //余弦相似度越大越相似
    return lhs.second > rhs.second;
}

vector<long> 
WebPageQuery::webSort(vector<double> &queryWordsWeights, vector<pair<long, vector<double>>> &resultVec) {
    //使用余弦相似度对包含所有关键词的网页进行排序
    vector<long> res;  //用来存放排好序的docId
    vector<pair<long, double>> docId2cosWeight; //用来存放每个docId对应的余弦相似度
    size_t sz = queryWordsWeights.size();

    for(auto & result : resultVec) {
        //计算每个结果网页和查询词的余弦相似度
        double XY = 0;
        double absXY = 0;
        for(size_t idx = 0; idx < sz; ++ idx) {
            XY = XY + queryWordsWeights[idx] * result.second[idx];
            absXY = absXY + fabs(queryWordsWeights[idx]) * fabs(result.second[idx]);
        }
        double cosWeight = XY / absXY;
        docId2cosWeight.push_back({result.first, cosWeight});
    }
    //调用sort算法，需使用自定义的比较器
    std::sort(docId2cosWeight.begin(), docId2cosWeight.end(), com);

    //把排好序的docid保存起来
    for(auto & docId : docId2cosWeight) {
        res.push_back(docId.first);
    }
    return res;
}

string WebPageQuery::returnNoAnswer() {
    //没有匹配的页面
    string res = "Not Found Anything!";
    return res;
}


void WebPageQuery::test(string query) {
    //测试
    string res = doQuery(query);
    cout << "========================\n";
    cout << res << endl;
    cout << "========================\n";
}

}//end of namespace gcj
